package com.runfast.common.web.controller;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.runfast.common.dao.model.RunfastCuser;
import com.runfast.common.dao.model.RunfastCuserExample;
import com.runfast.common.dao.model.RunfastLoginRecord;
import com.runfast.common.dao.model.RunfastLoginRecordExample;
import com.runfast.common.security.spring.MobilePasswordAuthenticationToken;
import com.runfast.common.security.spring.MobileSmsCodeAuthenticationToken;
import com.runfast.common.security.spring.ThirdAuthenticationToken;
import com.runfast.common.security.spring.UserDataDetails;
import com.runfast.common.service.RunfastCuserService;
import com.runfast.common.service.RunfastLoginRecordService;
import com.runfast.common.utils.JsonUtils;
import com.runfast.common.utils.SpringUtils;
import com.runfast.common.utils.TokenUtil;
import com.runfast.common.web.entity.Result;
import com.runfast.common.web.entity.ResultCode;
import com.runfast.common.web.shiro.LoginMode;
import com.runfast.common.web.shiro.ThirdLoginType;
import com.runfast.common.web.shiro.UrlPermission;
import com.runfast.waimai.common.Constants;
import com.runfast.waimai.dao.model.RunfastGoodsSellRecord;
import com.runfast.waimai.dao.model.RunfastSmsSendrecord;
import com.runfast.waimai.dao.model.RunfastSmsSendrecordExample;
import com.runfast.waimai.entity.Cart;
import com.runfast.waimai.entity.CartItem;
import com.runfast.waimai.service.RunfastSmsSendrecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.RandomStringGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

@RestController
@RequestMapping("/api/user/account")
@Slf4j
public class AccountController {


    @Autowired
    private RunfastCuserService cuserService;

    @Autowired
    private RunfastLoginRecordService loginRecordService;

    @Autowired
    private RunfastSmsSendrecordService smsSendrecordService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Value("${pay.account.mini.appId}")
    private String appId;

    @Value("${pay.account.mini.appSecret}")
    private String secret;

    @Autowired
    private RestTemplate restTemplate;
    @Resource
    private RedisTemplate redisTemplate;

    private void handleAlias(String alias,Integer loginOn,Integer userId) {

            //cuserService.updateAlias(alias);

            /**
             *将当前设备的其他账户退出,即logoutTime置为当前时间
             */
            RunfastLoginRecordExample loginRecordExample = new RunfastLoginRecordExample();
            if (StringUtils.isNotBlank(alias))
                loginRecordExample.or().andAccountTypeEqualTo(0).andAliasEqualTo(alias).andLogoutTimeIsNull();
            else
                loginRecordExample.or().andAccountTypeEqualTo(0).andAccountIdEqualTo(userId).andLogoutTimeIsNull();
            RunfastLoginRecord loginRecordUpdate = new RunfastLoginRecord();
            loginRecordUpdate.setLogoutTime(new Date());
            loginRecordService.updateByExampleSelective(loginRecordUpdate, loginRecordExample);

            /**
             * 保存登陆记录
             */
            RunfastLoginRecord loginRecord = new RunfastLoginRecord();
            loginRecord.setAccountType(0);
            loginRecord.setLoginTime(new Date());
            if (StringUtils.isNotBlank(alias)){
                UserDataDetails<RunfastCuser> userDataDetails = (UserDataDetails<RunfastCuser>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                RunfastCuser cuser = userDataDetails.getData();
                loginRecord.setAccountId(cuser.getId());
                loginRecord.setAlias(alias);
            }else{
                loginRecord.setAccountId(userId);
            }
            loginRecord.setLoginOn(loginOn);

            loginRecordService.insertSelective(loginRecord);


    }

    @RequestMapping("/logout/success")
    public Result logout() {
        return Result.ok("退出成功");
    }

    @PostMapping("/permissions")
    public Result permissions() {
        ApplicationContext applicationContext = SpringUtils.getApplicationContext();
        HashMap<String, Object> hashMap = new HashMap<>();
        extractMethodMappings(applicationContext, hashMap);
        return Result.ok("", hashMap);
    }

    private void extractMethodMappings(ApplicationContext applicationContext,
                                       Map<String, Object> result) {
        if (applicationContext != null) {
            for (Map.Entry<String, AbstractHandlerMethodMapping> bean : applicationContext
                    .getBeansOfType(AbstractHandlerMethodMapping.class).entrySet()) {
                @SuppressWarnings("unchecked")
                Map<?, HandlerMethod> methods = bean.getValue().getHandlerMethods();
                for (Map.Entry<?, HandlerMethod> method : methods.entrySet()) {
                    Map<String, String> map = new LinkedHashMap<String, String>();
                    map.put("bean", bean.getKey());
                    HandlerMethod handlerMethod = method.getValue();
                    UrlPermission methodAnnotation = handlerMethod.getMethodAnnotation(UrlPermission.class);
                    if (methodAnnotation != null) {
                        map.put("method", handlerMethod.toString());
                        result.put(method.getKey().toString(), map);

                    }


                }
            }
        }
    }


    private String generateToken(RunfastCuser cuser) {
        JWTCreator.Builder builder = JWT.create();
        String accessToken = null;
        try {
            RandomStringGenerator generator = new RandomStringGenerator.Builder()
                    .withinRange('a', 'z').build();
            String randomLetters = generator.generate(9);


            builder.withIssuer(TokenUtil.ISSUER)
                    .withClaim(TokenUtil.USER_ID, cuser.getId())
                    .withClaim("r", randomLetters)
                    .withIssuedAt(new Date())
                    .withExpiresAt(new Date(System.currentTimeMillis() + 30 * 60 * 60 * 24 * 1000L));
            accessToken = builder.sign(Algorithm.HMAC256(TokenUtil.SCRETE));

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return accessToken;
    }


    /**
     * 通用登陆
     *
     * @param thirdLoginId
     * @param thirdLoginType
     * @param alias
     * @param request
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestParam String loginMode, @RequestParam(required = false) String mobile, @RequestParam(required = false) String password, @RequestParam(required = false) String smsCode, @RequestParam(required = false) String thirdLoginId, @RequestParam(required = false) String thirdLoginType, @RequestParam(required = false) String alias, @RequestParam String deviceId, HttpServletRequest request) {
        AbstractAuthenticationToken authRequest = null;
        LoginMode loginModeEnum = LoginMode.valueOf(loginMode);
        switch (loginModeEnum) {
            case mobile_pwd:
                authRequest = new MobilePasswordAuthenticationToken(mobile, password, Collections.emptyList());
                break;
            case mobile_sms_code: //短信验证码登录，如果该手机号还未注册，将自动注册并登录
                authRequest = new MobileSmsCodeAuthenticationToken(mobile, smsCode, Collections.emptyList());
                break;
            case third: //通过第三方微信或者qq登录
                authRequest = new ThirdAuthenticationToken(thirdLoginId, thirdLoginType, Collections.emptyList());
                break;
        }
        AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));

        try {
            Authentication authenticate = authenticationManager.authenticate(authRequest);

            SecurityContextHolder.getContext().setAuthentication(authenticate);
        } catch (Exception e) {

            return Result.fail(ResultCode.FAIL, e.getLocalizedMessage());
        }

        /**
         * 处理推送别名
         */
        handleAlias(alias,RunfastGoodsSellRecord.LoginOn.app.getCode(),null);


        UserDataDetails<RunfastCuser> principal = (UserDataDetails<RunfastCuser>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        RunfastCuser cuser = principal.getData();
        Map<String, Object> map = new HashMap<>();
        map.put("token", generateToken(cuser));
        map.put("user", cuser);

        handleLogoutCart(deviceId, cuser);


        return Result.ok("登录成功", map);

    }

    /**
     * 处理未登陆添加的购物车
     *
     * @param deviceId
     * @param cuser
     */
    private void handleLogoutCart(String deviceId, RunfastCuser cuser) {
        /**
         * 将未登陆添加的购物车信息保存至当前登陆用户下
         */
        HashOperations hashOperations = redisTemplate.opsForHash();
        String deviceKey = Constants.REDIS_NAMESPACE_CART + Constants.DEVICE_ID_PREFIX + deviceId;
        String userKey = Constants.REDIS_NAMESPACE_CART + cuser.getId();
        Map<Object, Object> entries = (Map<Object, Object>) hashOperations.entries(deviceKey);
        if (!entries.isEmpty()) {
            boolean cleared = false; //是否已清空购物车
            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                Integer businessId = (Integer) entry.getKey();
                Cart cart = (Cart) entry.getValue();
                if (cart != null) {
                    List<CartItem> cartItems = cart.getCartItems();
                    if (cartItems != null && !cartItems.isEmpty()) {
                        if (!cleared) {
                            hashOperations.delete(userKey, businessId);
                            cleared = true;
                        }

                        hashOperations.put(userKey, businessId, cart);  //保存至用户自己的购物车中

                    }


                }


            }

        }

        /**
         * 购物车信息保存完毕，清除未登陆的购物车信息
         */
        hashOperations.delete(deviceKey, "*");
    }


    /**
     * 通过短信验证码注册并绑定第三方账户
     *
     * @param mobile
     * @param smsCode
     * @param thirdLoginId
     * @param thirdLoginType
     * @param alias
     * @return
     */
    @PostMapping("/registerAndBind")
    @ResponseBody
    public Result registerAndBind(@RequestParam String mobile, @RequestParam String smsCode, @RequestParam(required = false) String password, @RequestParam String thirdLoginId, @RequestParam String thirdLoginType, @RequestParam(required = false) String alias, @RequestParam(required = false) String deviceId) {
        RunfastCuserExample cuserExample = new RunfastCuserExample();
        cuserExample.or().andMobileEqualTo(mobile);
        List<RunfastCuser> cusers = cuserService.selectByExample(cuserExample);
        RunfastCuser cuser = null;
        if (cusers.isEmpty()) { //该手机号不存在，自动注册新账号

            if (StringUtils.isBlank(password)) return Result.fail(ResultCode.FAIL, "密码不能为空");

            RunfastSmsSendrecordExample smsSendrecordExample = new RunfastSmsSendrecordExample();
            smsSendrecordExample.or().andSmstypeEqualTo(RunfastSmsSendrecord.SmsType.sms_bind.getCode()).andPhoneEqualTo(mobile).andXcodeEqualTo(smsCode);
            smsSendrecordExample.setOrderByClause("id desc");
            List<RunfastSmsSendrecord> smsSendrecordList = smsSendrecordService.selectByExample(smsSendrecordExample);
            if (smsSendrecordList.isEmpty()) return Result.fail(ResultCode.FAIL, "验证码错误");

            cuser = new RunfastCuser();
            cuser.setMobile(mobile);
            cuser.setPassword(password);
            cuser.setCreateTime(new Date());

            ThirdLoginType loginType = ThirdLoginType.valueOf(thirdLoginType);
            switch (loginType) {
                case QQ:
                    cuser.setQqId(thirdLoginId);
                    break;
                case WEIXIN:
                    cuser.setWeixinId(thirdLoginId);
                    break;
                case MINI:
                    cuser.setMiniOpenId(thirdLoginId);
            }


            cuserService.insertSelective(cuser);

        } else {

            /**
             * 更新三方登陆信息
             */
            cuser = cusers.get(0);

            RunfastCuser cuserUpdate = new RunfastCuser();
            cuserUpdate.setId(cuser.getId());
            ThirdLoginType loginType = ThirdLoginType.valueOf(thirdLoginType);
            switch (loginType) {
                case QQ:
                    cuserUpdate.setQqId(thirdLoginId);
                    break;
                case WEIXIN:
                    cuserUpdate.setWeixinId(thirdLoginId);
                    break;
                case MINI:
                    cuserUpdate.setMiniOpenId(thirdLoginId);
            }

            cuserService.updateByPrimaryKeySelective(cuserUpdate);


        }

        password = cuser.getPassword();

        /**
         * 内部登陆
         */
        MobilePasswordAuthenticationToken mobilePasswordAuthenticationToken = new MobilePasswordAuthenticationToken(mobile, password, null);

        try {
            Authentication authenticate = authenticationManager.authenticate(mobilePasswordAuthenticationToken);

            SecurityContextHolder.getContext().setAuthentication(authenticate);
        } catch (Exception e) {

            return Result.fail(ResultCode.FAIL, e.getLocalizedMessage());
        }

        /**
         * 处理推送别名
         */
        ThirdLoginType loginType = ThirdLoginType.valueOf(thirdLoginType);
        switch (loginType) {
            case QQ:
                handleAlias(alias,RunfastGoodsSellRecord.LoginOn.app.getCode(),null);
                break;
            case WEIXIN:
                handleAlias(alias,RunfastGoodsSellRecord.LoginOn.app.getCode(),null);
                break;
            case MINI:
                cuserExample = new RunfastCuserExample();
                cuserExample.or().andMiniOpenIdEqualTo(thirdLoginId);
                List<RunfastCuser> cuserList = cuserService.selectByExample(cuserExample);
                if (cuserList.isEmpty())
                    return Result.fail(ResultCode.MINI_ACCOUNT_NOT_BIND, ResultCode.MINI_ACCOUNT_NOT_BIND.getDescription(), thirdLoginId);

                cuser = cuserList.get(0);
                handleAlias(alias,RunfastGoodsSellRecord.LoginOn.miniwechat.getCode(),cuser.getId());
        }


        UserDataDetails<RunfastCuser> principal = (UserDataDetails<RunfastCuser>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        RunfastCuser user = principal.getData();
        Map<String, Object> map = new HashMap<>();
        map.put("token", generateToken(cuser));
        map.put("user", user);

        handleLogoutCart(deviceId, cuser);
        return Result.ok("", map);
    }


    @PostMapping("/register")
    @ResponseBody
    public Result register(@RequestParam String mobile, @RequestParam String smsCode, @RequestParam String password, @RequestParam(required = false) String alias, @RequestParam(required = false) String deviceId) {


        RunfastCuserExample cuserExample = new RunfastCuserExample();
        cuserExample.or().andMobileEqualTo(mobile);
        List<RunfastCuser> cusers = cuserService.selectByExample(cuserExample);
        if (!cusers.isEmpty()) return Result.fail(ResultCode.FAIL, "该手机号已经被注册");

        RunfastSmsSendrecordExample smsSendrecordExample = new RunfastSmsSendrecordExample();
        smsSendrecordExample.or().andSmstypeEqualTo(RunfastSmsSendrecord.SmsType.sms_register.getCode()).andPhoneEqualTo(mobile).andXcodeEqualTo(smsCode);
        smsSendrecordExample.setOrderByClause("id desc");
        List<RunfastSmsSendrecord> smsSendrecordList = smsSendrecordService.selectByExample(smsSendrecordExample);
        if (smsSendrecordList.isEmpty()) return Result.fail(ResultCode.FAIL, "验证码错误");

        RunfastCuser cuser = new RunfastCuser();
        cuser.setMobile(mobile);
        cuser.setPassword(password);
        cuser.setCreateTime(new Date());


        cuserService.insertSelective(cuser);

        /**
         * 内部登陆
         */
        MobilePasswordAuthenticationToken mobilePasswordAuthenticationToken = new MobilePasswordAuthenticationToken(mobile, password, null);

        try {
            Authentication authenticate = authenticationManager.authenticate(mobilePasswordAuthenticationToken);

            SecurityContextHolder.getContext().setAuthentication(authenticate);
        } catch (Exception e) {

            return Result.fail(ResultCode.FAIL, e.getLocalizedMessage());
        }


        /**
         * 处理推送别名
         */
        handleAlias(alias,RunfastGoodsSellRecord.LoginOn.app.getCode(),null);


        UserDataDetails<RunfastCuser> principal = (UserDataDetails<RunfastCuser>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        RunfastCuser user = principal.getData();
        Map<String, Object> map = new HashMap<>();
        map.put("token", generateToken(cuser));
        map.put("user", user);

        handleLogoutCart(deviceId, cuser);
        return Result.ok("注册账号成功", map);
    }


    @PostMapping("/mobileExist")
    @ResponseBody
    public Result mobileExist(@RequestParam String mobile) {


        RunfastCuserExample cuserExample = new RunfastCuserExample();
        cuserExample.or().andMobileEqualTo(mobile);
        long count = cuserService.countByExample(cuserExample);


        return count > 0 ? Result.ok("") : Result.fail(ResultCode.FAIL, "");
    }


    /**
     * 小程序登陆
     *
     * @param code
     * @return
     */
    @PostMapping("/miniLogin")
    public Result miniLogin(@RequestParam String code) {
        if (StringUtils.isBlank(code)) return Result.fail(ResultCode.PARAMETER_ERROR, "code 不能为空");

        HashMap<String, String> params = new HashMap<>();
        params.put("appid", appId);
        params.put("secret", secret);

        params.put("js_code", code);
        params.put("grant_type", "authorization_code");
        String responseStr = restTemplate.getForObject("https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type={grant_type}", String.class, params);

        try {
            Map map = JsonUtils.getMapper().readValue(responseStr, Map.class);
            String openid = (String) map.get("openid");

            if (StringUtils.isBlank(openid)) return Result.fail(ResultCode.FAIL, "获取openid失败");

            Object sessionKey = map.get("session_key");


            RunfastCuserExample cuserExample = new RunfastCuserExample();
            cuserExample.or().andMiniOpenIdEqualTo(openid);
            List<RunfastCuser> cuserList = cuserService.selectByExample(cuserExample);
            if (cuserList.isEmpty())
                return Result.fail(ResultCode.MINI_ACCOUNT_NOT_BIND, ResultCode.MINI_ACCOUNT_NOT_BIND.getDescription(), openid);

            RunfastCuser cuser = cuserList.get(0);


            /**
             * 处理推送别名
             */
            handleAlias(null,RunfastGoodsSellRecord.LoginOn.miniwechat.getCode(),cuser.getId());

            Map<String, Object> modelMap = new HashMap<>();
            modelMap.put("token", generateToken(cuser));
            modelMap.put("cuser", cuser);
            modelMap.put("openId", openid);


            return Result.ok("", modelMap);


        } catch (IOException e) {
            log.error(e.getLocalizedMessage(), e);
            return Result.fail(ResultCode.FAIL, "获取openid失败");
        }


    }


    /**
     * 验证短信验证码
     *
     * @param smsType
     * @param mobile
     * @param smsCode
     * @return
     */
    @PostMapping("/verifySmsCode")
    public Result verifySmsCode(@RequestParam RunfastSmsSendrecord.SmsType smsType, @RequestParam String mobile, @RequestParam String smsCode) {
        RunfastSmsSendrecordExample smsSendrecordExample = new RunfastSmsSendrecordExample();
        smsSendrecordExample.or().andSmstypeEqualTo(smsType.getCode()).andPhoneEqualTo(mobile).andXcodeEqualTo(smsCode);
        smsSendrecordExample.setOrderByClause("id desc");
        List<RunfastSmsSendrecord> smsSendrecordList = smsSendrecordService.selectByExample(smsSendrecordExample);
        if (smsSendrecordList.isEmpty()) return Result.fail(ResultCode.FAIL, "验证码错误");
        return Result.ok("");

    }

}
